/******************************************************************************
 * Student Name: Ofri Keidar
 * Exercise Name: Ex2
 * File Description: Class CourseLinkedList's implementation
 ******************************************************************************/

#include "CourseLinkedList.h"
#define INDEX_INIT_VAL 0 // used to initialize new nodes' index

//////////////////////////////////////////////////////////////////////////////////// class Node's methods

//////////////////////////////////////////////////////////////////// public methods

/***************************************************************************************
* function name: Node(Course* courseToPoint)
* The Input: Student data to point at
* The output: None
* The Function operation: Initializes course data pointer to point at given course.
*			              Previous and next node pointers are initialized as NULL and
*			              index is initialized as INDEX_INIT_VAL because the new node's
*			              position in the linked list is not set yet
***************************************************************************************/
CourseLinkedList::Node::Node(Course* courseToPoint): m_courseData(courseToPoint) {

	// new node is not yet linked to the list
	m_prevNode = NULL;
	m_nextNode = NULL;
	m_index = INDEX_INIT_VAL;
}

/****************************************************************************************
* function name: ~Node()
* The Input: None
* The output: None
* The Function operation: Deletes student data being pointed by the node.
*			              Next and previous nodes are linked and the next nodes'
*			              indices are decremented
***************************************************************************************/
CourseLinkedList::Node::~Node() {

	// link next node and previous node
	if (m_nextNode != NULL) { // not end of list
		m_nextNode->m_prevNode = m_prevNode;
	}
	if (m_prevNode != NULL) { // not beginning of list
		m_prevNode->m_nextNode = m_nextNode;
	}

	// decrement next nodes' index
	Node* currentNode = m_nextNode;
	while (currentNode != NULL) { // scan list to its end
		--(currentNode->m_index); // decrement index
		currentNode = currentNode->m_nextNode; // move to next node
	}
}

//////////////////////////////////////////////////////////////////////////////////// class Node's methods

//////////////////////////////////////////////////////////////////// private methods

/***************************************************************************************
* function name: findPlace(const Course& newCourse)
* The Input: Pointer to course to add
* The output: Node pointer to which the new course should be linked.
* 	          If a course with new course's number already exists, NULL is returned
* The Function operation: Uses 2 node pointers- one to point at previous node being
*			              scanned and another one to point at current node on list.
*	                      Method compares current course's number with given course's number.
*			  			  If numbers are equal, given course cannot be added and NULL is
*			  			  returned. If current course number is smaller than new course's,
*		                  a pointer to current node is returned
***************************************************************************************/
CourseLinkedList::Node* CourseLinkedList::findPlace(const Course& newCourse) const {

	Node* prev = m_headDummy; // pointer to previous node being scanned
	Node* curr = prev->m_nextNode; // pointer to current node being scanned

	while (prev->m_nextNode->m_courseData != NULL) {

		if (newCourse.getCourseNumber() < curr->m_courseData->getCourseNumber()) {
			return curr;
		}
		// move to next node
		prev = curr;
		curr = curr->m_nextNode;
	}
	return curr;
}

/***************************************************************************************
* function name: internalAdd(Course& newCourse, Node* before)
* The Input: Pointer to course to be added and a pointer to the node where the new
* 	         student will be linked
* The output: None
* The Function operation: A new node is created pointing to course to be added.
*			              Method links the new node before the given node and then
*			              increments next nodes' indices
***************************************************************************************/
void CourseLinkedList::internalAdd(Course& newCourse, Node* before) {

	Node* newNode = new Node(&newCourse); // create a node pointing to new student
	// link node to list
	Node* temp = before->m_prevNode;
	newNode->m_nextNode = before;
	before->m_prevNode = newNode;

	newNode->m_prevNode = temp;
	temp->m_nextNode = newNode;

	++(m_tailDummy->m_index); // increment tail's index
	// update new node's index
	if (before == m_tailDummy) {
		newNode->setIndex(m_tailDummy->m_index);
	} else {
		newNode->setIndex((before->m_index));
	}

	// increment index of next nodes
	while (before != m_tailDummy) {
		before->setIndex((before->m_index)+1);
		before = before->m_nextNode;
	}

}

//////////////////////////////////////////////////////////////////// public methods

/***************************************************************************************
* function name: CourseLinkedList(const StudentLinkedList& studentDataBase, const LecturerLinkedList& lecturerDataBase)
* The Input: None
* The output: None
* The Function operation: List's head and tail are pointers to nodes which do not point
*			  			  at courses (point at NULL). This implementation prevents the
*			  			  need to check if previous/next nodes are NULL- makes code
*		             	  more quickly because there are less conditions to check
***************************************************************************************/
CourseLinkedList::CourseLinkedList(const StudentLinkedList& studentDataBase, const LecturerLinkedList& lecturerDataBase): m_studentDataBase(studentDataBase) ,m_lecturerDataBase(lecturerDataBase) {

	// create dummy nodes to avoid checking NULL values
	m_headDummy = new Node(NULL);
	m_tailDummy = new Node(NULL);

	m_headDummy->m_nextNode = m_tailDummy;
	m_tailDummy->m_prevNode = m_headDummy;
	m_currentNode = m_headDummy;
}

/***************************************************************************************
* function name: ~CourseLinkedList()
* The Input: None
* The output: None
* The Function operation: Scans list from start to end and deletes each node
***************************************************************************************/
CourseLinkedList::~CourseLinkedList() {

	// scan list and delete nodes
	Node* currNode = m_headDummy;
	Node* prevNode = m_headDummy;

	while (currNode != NULL) {
		// move to next node on list and delete the previous which has been scanned
		currNode = currNode->m_nextNode;
		delete prevNode;
		prevNode = currNode;
	}
	delete prevNode; // delete last node on list
}

/***************************************************************************************
 * function name: AddCourseRetVals addCourse(const int courseNumber, const int lecturerID)
 * The Input: New course number and new course's lecturer id
 * The output: COURSE_EXISTS if in course list already exists a course with given course
 * 			   number, NO_SUCH_LECTURER if there is not a lecturer with given id,
 * 			   COURSE_ADDED if course was successfully added to course list
 * The Function operation: Uses searchByKey to check if there is a course with given number.
 *						   If there is, COURSE_EXISTS is returned and course is not added.
 *						   If not, method uses lecturer database's searchByKey method to
 *						   check if there is a lecturer with given id. If there is not,
 *						   NO_SUCH_LECTURER is returned and course is not added.
 *						   Otherwise, method creates a new course with given attributes,
 *						   find were to link it in list (by using findPlace) and links it
 *						   (using internalAdd).
 ***************************************************************************************/
CourseLinkedList::AddCourseRetVals CourseLinkedList::addCourse(const int courseNumber, const int lecturerID) {

	// check if a course with the same number already exists
	Course* newCourse = searchByKey(courseNumber);
	if (newCourse != NULL) { // no such course
		return COURSE_EXISTS;
	}

	// course exists, now check if lecturer exists
	Lecturer* newLecturer = m_lecturerDataBase.searchByKey(lecturerID);
	if (newLecturer == NULL) { // no such lecturer
		return NO_SUCH_LECTURER;
	}

	// add course to list
	newCourse  = new Course(courseNumber, newLecturer, m_studentDataBase); // create a new course with given values
	Node* after = findPlace(*newCourse); // find where to add the new course
	internalAdd(*newCourse, after); // add course

	return COURSE_ADDED;
}

/***************************************************************************************
* function name: addStudentsToCourse(const int courseNumber, const int numOfStudents, int** idList)
* The Input: Course number to which students should be added, number of students to add
* 			 and a list of their id's
* The output: NUMBER_ERROR_VAL if no such course exist or number of students who were
* 			  not added
* The Function operation: If course does not exist, return NUMBER_ERROR_VAL.
* 					      Checks if each id on given list:
* 						  Searches the id in student database to check if student exist,
* 						  if so- continues, if not- False result is set to this id on
* 						  the id list.
* 						  If id exists in database, the method searches this id in
* 						  registered students list. If it appears, student cannot be
* 						  added again and False result is set to this id on the id list.
*                         If the id does not appear, registered students list's adding
*                         method is used in order to add the id to the list.
*                         Return number of students who were not added
***************************************************************************************/
int CourseLinkedList::addStudentsToCourse(const int courseNumber, const int numOfStudents, int** idList) {

	// check if a course with given number exists
	Course* courseToChange = searchByKey(courseNumber);

	if (courseToChange == NULL) { // no such course
		return NUMBER_ERROR_VAL;
	}

	// try adding students to given course
	int mismatchCtr = courseToChange->addStudent(numOfStudents, idList);
	return mismatchCtr;
}

/***************************************************************************************
* function name: incrmntCurrent()
* The Input: None
* The output: False if end is reached, True if not
* The Function operation: If current node is list's tail, False is returned because
*			  			  there is no next node to move to.
*		                  If current node is not list's tail, node is incremented.
*			              If now current node is list's tail, False is returned.
*		                  Otherwise, True is returned
***************************************************************************************/
bool CourseLinkedList::incrmntCurrent() {
	// already at list's end, return FALSE
	if (m_currentNode == m_tailDummy) {
		return false;
	}

	m_currentNode = m_currentNode->m_nextNode; // increment current node pointer to next node on list

	// reached on of list and return FALSE
	if (m_currentNode == m_tailDummy) {
		return false;
	}

	// has not reached end yet, return TRUE
	return true;
}

/***************************************************************************************
* function name: getCurrentNumber()
* The Input: None
* The output: Number of course which current node pointer points at.
* 	          If current node points at no course, NUMBER_ERROR_VAL is returned
* The Function operation: Checks if course pointer is NULL. If so, returned NUMBER_ERROR_VAL,
*                         otherwise uses the course's getCourseNumber method in order to return
*                         the course's number
***************************************************************************************/
int CourseLinkedList::getCurrentNumber() const {
	// list is empty or reached end of list
	if (m_currentNode->m_courseData == NULL) {
		return NUMBER_ERROR_VAL;
	}
	return m_currentNode->m_courseData->getCourseNumber(); // return current course's number
}

/***************************************************************************************
* function name: getCurrentLecName()
* The Input: None
* The output: Name of course lecturer which current node pointer points at.
* 	          If current node points at no course, NAME_ERROR_VAL is returned
* The Function operation: Checks if course pointer is NULL. If so, returned NAME_ERROR_VAL,
*                         otherwise uses the getLecturerName method is used in order to
*                         return the lecturer's name
***************************************************************************************/
string CourseLinkedList::getCurrentLecName() const {
	// list is empty or reached end of list
	if (m_currentNode->m_courseData == NULL) {
		return NAME_ERROR_VAL;
	}
	return m_currentNode->m_courseData->getLecturerName(); // return current course's lecturer's name
}

/***************************************************************************************
* function name: getCurrentLecID()
* The Input: None
* The output: Id of course lecturer which current node pointer points at.
* 	          If current node points at no course, ID_ERROR_VAL is returned
* The Function operation: Checks if course pointer is NULL. If so, returned ID_ERROR_VAL,
*                         otherwise uses the getLecturerID method is used in order to
*                         return the lecturer's id
***************************************************************************************/
int CourseLinkedList::getCurrentLecID() const {
	// list is empty or reached end of list
	if (m_currentNode->m_courseData == NULL) {
		return ID_ERROR_VAL;
	}
	return m_currentNode->m_courseData->getLecturerID(); // return current course's lecturer's ID
}

/***************************************************************************************
* function name: searchByKey(const int key)
* The Input: Course number to look for
* The output: Pointer to course whose number matches given value. If no such course
* 			  exists, NULL is returned.
* The Function operation: Initializes a node pointer to point at list's head. Method
*			  			  compares given value with the number of the course being
*                         pointed by current node. Scans stop once such course is found
*                         or list's end is reached. If a course is found, a pointer to
*                         the course is returned, otherwise NULL is returned
***************************************************************************************/
Course* CourseLinkedList::searchByKey(const int key) const {

	// scan list and find the student matching given key
	Node* currNode = m_headDummy->m_nextNode;
	while (currNode->m_courseData != NULL) {
		if (currNode->m_courseData->getCourseNumber() == key) {
			return currNode->m_courseData;
		}
		currNode = currNode->m_nextNode; // move to next node
	}
	return NULL; // no match found
}

/***************************************************************************************
* function name: searchByIndex(const int index)
* The Input: Index to look for
* The output: Pointer to course whose index in list matches given value. If no such course
* 			  exists, NULL is returned.
* The Function operation: Initializes a node pointer to point at list's head. Method
*			  			  compares given value with the index of node pointed by current
*                         current node pointer. Scans stop once such course is found
*                         or list's end is reached. If a course is found, a pointer to
*                         the course is returned, otherwise NULL is returned
***************************************************************************************/
Course* CourseLinkedList::searchByIndex(const int index) const {

	// scan list and find the student whose node matching given index
	Node* currNode = m_headDummy;
	while (currNode != NULL) {
		if (currNode->m_index == index) {
			return currNode->m_courseData;
		}
		currNode = currNode->m_nextNode; // move to next node
	}
	return NULL; // no match found
}
